﻿using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using SelfService.Model;
using SelfService.Model.Models;
using WalkingTec.Mvvm.Core;

namespace SelfService.Services;

public class TaskService : BackgroundService
{
    private readonly IServiceProvider _provider;

    public TaskService(IServiceProvider provider)
    {
        _provider = provider;
    }

    private static readonly ConcurrentDictionary<Guid, object> RunList = new();

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        return Task.Run(async () =>
        {
            var scope = _provider.CreateScope();
            var wtm = scope.ServiceProvider.GetRequiredService<WTMContext>();
            do
            {
                try
                {
                    var list = await wtm.DC.Set<SysTask>()
                        .AsNoTracking()
                        .Where(e => e.IsEnable)
                        .ToListAsync(cancellationToken: stoppingToken);
                    foreach (var task in list)
                    {
                        if (task.NextRunTime < DateTime.Now)
                        {
                            if (RunList.TryAdd(task.ID, task))
                            {
                                Process(task);
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    wtm.DoLog($"任务调度线程异常:{JsonConvert.SerializeObject(e)}", ActionLogTypesEnum.Exception, "TaskService");
                }
                await Task.Delay(100, stoppingToken);
            } while (!stoppingToken.IsCancellationRequested);
            wtm.DoLog($"任务调度线程停止运行", ActionLogTypesEnum.Exception, "TaskService");
        }, stoppingToken);
    }
    
    private void Process(SysTask task)
    {
        Task.Run(async () =>
        {
            var scope = _provider.CreateScope();
            var wtm = scope.ServiceProvider.GetRequiredService<WTMContext>();
            try
            {
                var sw = new Stopwatch();
                sw.Start();
                Type type = Type.GetType("SelfService.Tasks." + task.ClassName);
                if (type != null)
                {
                    object obj = default;
                    var data = wtm.DC.Set<SysTask>().FirstOrDefault(e => e.ID == task.ID);
                    if (data == null) return;
                    try
                    {
                        obj = scope.ServiceProvider.GetRequiredService(type);
                    }
                    catch (Exception)
                    {
                        wtm.DoLog($"任务:{task.Name}没有找到对应的执行类{task.ClassName}", ActionLogTypesEnum.Exception,
                            "TaskService");
                        data.IsEnable = false;
                        await wtm.DC.SaveChangesAsync();
                        sw.Stop();
                        return;
                    }

                    if (obj is BaseTask instance)
                    {
                        data.LastRunTime = DateTime.Now;
                        data.LastEndTime = null;
                        data.LastRunResult = null;
                        if (task.TaskType is TaskTypeEnum.定时任务)
                        {
                            DateTime now = DateTime.Now;
                            switch (data.TimeType)
                            {
                                case TimeTypeEnum.天:
                                    data.NextRunTime = DateTime.Today.AddDays(1).Add(data.Interval);
                                    break;
                                case TimeTypeEnum.周:
                                    data.NextRunTime = DateTime.Today.AddDays(7).Add(data.Interval);
                                    break;
                                case TimeTypeEnum.月:
                                    data.NextRunTime = new DateTime(now.Year, now.Month, 1).AddMonths(1)
                                        .Add(data.Interval);
                                    break;
                                case TimeTypeEnum.年:
                                    data.NextRunTime = new DateTime(now.Year, 1, 1).AddYears(1)
                                        .Add(data.Interval);
                                    break;
                            }
                        }

                        if (task.TaskType is TaskTypeEnum.单次任务)
                        {
                            data.IsEnable = false;
                            data.NextRunTime = null;
                        }

                        await wtm.DC.SaveChangesAsync();

                        try
                        {
                            var result = await instance.Process();
                            sw.Stop();
                            data.LastRunResult = result ? TaskResultEnum.成功 : TaskResultEnum.失败;
                        }
                        catch (Exception e)
                        {
                            sw.Stop();
                            data.LastRunResult = TaskResultEnum.失败;
                            wtm.DoLog($"任务{data.Name}发生异常:{JsonConvert.SerializeObject(e)}",
                                ActionLogTypesEnum.Exception, "TaskService");
                        }
                        if (data.TaskType is TaskTypeEnum.周期任务)
                        {
                            var time = data.Interval - sw.Elapsed;
                            data.NextRunTime = time.Milliseconds > 0 ? DateTime.Now.Add(time) : DateTime.Now;
                        }
                        data.LastEndTime = DateTime.Now;
                        await wtm.DC.SaveChangesAsync();
                    }
                }
            }
            catch (Exception e)
            {
                wtm.DoLog($"执行任务{task.Name}发生异常:{JsonConvert.SerializeObject(e)}", ActionLogTypesEnum.Exception,
                    "TaskService");
            }
            finally
            {
                if (RunList.TryRemove(task.ID, out _))
                {
                    wtm.DoLog($"任务【{task.Name}】执行完成", ActionLogTypesEnum.Job,
                        "TaskService");
                }
                else
                {
                    wtm.DoLog($"任务【{task.Name}】从缓存中移除失败", ActionLogTypesEnum.Exception,
                        "TaskService");
                }
            }
        });
    }
}